﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Autofac;
using Autofac.Builder;
using Autofac.Configuration;
using Dexter.Dependency.Autofac.Extensions;
using Dexter.Dependency.Autofac.Module;

namespace Dexter.Dependency.Autofac {
	public class AutofacContainer : IDexterContainer {
		IContainer container;
		readonly ContainerBuilder builder;

		public AutofacContainer ( ) {
			builder = new ContainerBuilder ( );
			builder.RegisterModule ( new LoggingModule ( ) );
		}

		public AutofacContainer ( IContainer container ) {
			this.container = container;
		}

		public IContainer Container {
			get {
				if(container == null)
					container = builder.Build ( );
				
				return container;
			}
			set { container = value; }
		}

		public void Shutdown ( ) {
			if (Container != null) {
				Container.Dispose ( );
				container = null;
			}
		}

		public void Configure ( string filePath ) {
			if(!string.IsNullOrEmpty(filePath))
				builder.RegisterModule ( new ConfigurationSettingsReader ( "mycomponents", filePath ) );
		}

		public object Resolve ( Type type ) {
			return Container.Resolve ( type );
		}

		public object Resolve ( Type type , string key ) {
			return Container.ResolveNamed ( key, type );
		}

		public T Resolve <T> ( ) {
			return Container.Resolve<T> ( );
		}

		public T Resolve <T> ( string key ) {
			return Container.ResolveNamed<T> ( key );
		}

		public void Register ( string key , Type type , object instance , LifeCycle lifeCycle ) {
			switch (lifeCycle) {
				case LifeCycle.Transient:
					builder.Register ( c => instance ).As ( type ).Named ( key, type ).InstancePerDependency ( );
					break;
				case LifeCycle.Thread:
					builder.Register ( c => instance ).As ( type ).Named ( key, type ).InstancePerLifetimeScope ( );
					break;
				default:
					builder.Register ( c => instance ).As ( type ).Named ( key, type ).SingleInstance ( );
					break;
			}
		}

		public void Register ( string key , Type type , Type implementedBy , LifeCycle lifeCycle ) {
			switch (lifeCycle) {
				case LifeCycle.Transient:
					builder.RegisterType ( implementedBy ).As ( type ).Named ( key, type ).InstancePerDependency ( );
					break;
				case LifeCycle.Thread:
					builder.RegisterType ( implementedBy ).As ( type ).Named ( key, type ).InstancePerLifetimeScope ( );
					break;
				default:
					builder.RegisterType ( implementedBy ).As ( type ).Named ( key, type ).SingleInstance ( );
					break;
			}
		}

		public void Register <T , TK> ( string key , TK instance , LifeCycle lifeCycle ) where TK : class , T {
			switch (lifeCycle) {
				case LifeCycle.Transient:
					builder.Register ( c => instance ).As<T> (  ).Named<T> ( key ).InstancePerDependency ( );
					break;
				case LifeCycle.Thread:
					builder.Register ( c => instance ).As<T> ( ).Named<T> ( key ).InstancePerLifetimeScope ( );
					break;
				default:
					builder.Register ( c => instance ).As<T> ( ).Named<T> ( key ).SingleInstance ( );
					break;
			}
		}

		public void Register <T , TK> ( string key , LifeCycle lifeCycle ) where TK : class , T {
			switch (lifeCycle) {
				case LifeCycle.Transient:
					builder.RegisterType<TK> ( ).As<T> ( ).Named<T> ( key ).InstancePerDependency ( );
					break;
				case LifeCycle.Thread:
					builder.RegisterType<TK> ( ).As<T> ( ).Named<T> ( key ).InstancePerLifetimeScope ( );
					break;
				default:
					builder.RegisterType<TK> ( ).As<T> ( ).Named<T> ( key ).SingleInstance ( );
					break;
			}
		}

		public void RegisterComponentsByBaseClass <T> ( Assembly assembly , LifeCycle lifeCycle ) {
			Type baseClass = typeof ( T );

			IEnumerable<Type> types = assembly.GetTypes ( ).Where ( c => !c.IsAbstract && baseClass.IsAssignableFrom ( c ) );

			foreach (Type controller in types) {
				if (controller == null || string.IsNullOrEmpty ( controller.FullName )) {
					continue;
				}

				Register ( controller.FullName.ToLowerInvariant ( ), controller, controller, lifeCycle );
			}
		}

		public void AddRegistration ( string key , Type type , object instance , LifeCycle lifeCycle ) {
			var b = new ContainerBuilder ( );
			switch (lifeCycle) {
				case LifeCycle.Transient:
					b.Register ( c => instance ).As ( type ).Named ( key, type ).InstancePerDependency ( );
					break;
				case LifeCycle.Thread:
					b.Register ( c => instance ).As ( type ).Named ( key, type ).InstancePerLifetimeScope ( );
					break;
				default:
					b.Register ( c => instance ).As ( type ).Named ( key, type ).SingleInstance ( );
					break;
			}

			b.Update ( container );
		}

		public void AddRegistration ( string key , Type type , Type implementedBy , LifeCycle lifeCycle ) {
			var b = new ContainerBuilder ( );

			switch (lifeCycle) {
				case LifeCycle.Transient:
					b.RegisterType ( implementedBy ).As ( type ).Named ( key, type ).InstancePerDependency ( );
					break;
				case LifeCycle.Thread:
					b.RegisterType ( implementedBy ).As ( type ).Named ( key, type ).InstancePerLifetimeScope ( );
					break;
				default:
					b.RegisterType ( implementedBy ).As ( type ).Named ( key, type ).SingleInstance ( );
					break;
			}

			b.Update ( container );
		}

		public void AddRegistration <T , TK> ( string key , TK instance , LifeCycle lifeCycle ) where TK : class , T {
			var b = new ContainerBuilder ( );
			
			switch (lifeCycle) {
				case LifeCycle.Transient:
					b.Register ( c => instance ).As<T> ( ).Named<T> ( key ).InstancePerDependency ( );
					break;
				case LifeCycle.Thread:
					b.Register ( c => instance ).As<T> ( ).Named<T> ( key ).InstancePerLifetimeScope ( );
					break;
				default:
					b.Register ( c => instance ).As<T> ( ).Named<T> ( key ).SingleInstance ( );
					break;
			}

			b.Update ( container );
		}

		public void AddRegistration <T , TK> ( string key , LifeCycle lifeCycle ) where TK : class , T {
			var b = new ContainerBuilder ( );
			
			switch (lifeCycle) {
				case LifeCycle.Transient:
					builder.RegisterType<TK> ( ).As<T> ( ).Named<T> ( key ).InstancePerDependency ( );
					break;
				case LifeCycle.Thread:
					builder.RegisterType<TK> ( ).As<T> ( ).Named<T> ( key ).InstancePerLifetimeScope ( );
					break;
				default:
					builder.RegisterType<TK> ( ).As<T> ( ).Named<T> ( key ).SingleInstance ( );
					break;
			}

			b.Update ( container );
		}

		public void AddRegistrationComponentsByBaseClass <T> ( Assembly assembly , LifeCycle lifeCycle ) {
			Type baseClass = typeof ( T );

			IEnumerable<Type> types = assembly.GetTypes ( ).Where ( c => !c.IsAbstract && baseClass.IsAssignableFrom ( c ) );

			foreach (Type controller in types) {
				if (controller == null || string.IsNullOrEmpty ( controller.FullName )) {
					continue;
				}

				AddRegistration ( controller.FullName.ToLowerInvariant ( ), controller, controller, lifeCycle );
			}
		}

		public T[] ResolveAll <T> ( ) {
			return Container.ResolveAll <T> ( ).ToArray (  );
		}

		public object[] ResolveAll ( Type type ) {
			return Container.ResolveAll ( type ).ToArray (  );
		}
	}
}